package com.zjl.学习.第02章_作业对象Job;

import com.zjl.springbatch.util.OutColour;
import com.zjl.学习.第01章_入门.F_最简单Batch案例;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.CompositeJobParametersValidator;
import org.springframework.batch.core.job.DefaultJobParametersValidator;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.DataFieldMaxValueJobParametersIncrementer;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.listener.JobListenerFactoryBean;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.scope.context.StepContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

/**
 *
 *  Job Instance = Job名称 + 识别参数
 *  Job Instance 一次执行创建一个 Job Execution对象
 *  完整的一次Job Instance 执行可能创建一个Job Execution对象，也可能创建多个Job Execution 对象
 *
 * 每次执行 job_instance  表中会保存 Job的名称 + 识别参数， 只会生成一条
 * 每次执行 job_execution 每次执行都会生成数据，表示他的job成功还是失败等等
 * @see com.zjl.学习.第01章_入门.F_最简单Batch案例
 *
 * 识别参数怎样执行 就是 main(String[] args)
 * 中的 args 的值，比如
 *      args = new String[]{"name=test2"};
 *    这样的话, batch_job_execution_params 这张表 就会有数据了
 *    而且 相同的 Job名称，也会多次执行，因为 Job Instance = Job名称 + 识别参数
 *    识别参数不一致 或者
 *        new JobParametersBuilder()
 *                 .addDate("time",new Date()); 添加时间参数
 *
 * ************原因：Spring Batch 相同Job名与相同标识参数只能成功执行一次。**************
 */
@SpringBootApplication
@EnableBatchProcessing//开启spring batch
public class C_Job的参数JobParameters {

    /**
     * 这种 Job 相同名字的情况下，即 .get("job002") 的名字相同。
     * 且这是个自启动项目，即配置文件没有设置
     * ------------------------------------
     *  spring:
     *    batch:
     *      job:
     *        enabled: true  #true这里是设置会自动先执行一次定义的job
     *  --------------------------------------------
     *  且 设置了 .incrementer(new RunIdIncrementer())
     *  //放入 作业递增 run.id 参数 或其他 继承了 JobParametersIncrementer 的 作业参数增量表
     *  都会 自己从数据库中找 识别参数 会将最近的 job 相同名字的所有参数读取出来 进行参数替换
     *  识别参数 (Job Instance = Job名称 + 识别参数)
     *  如 new RunIdIncrementer()
     *  会将最近的 job 相同名字的所有参数读取出来， run.id + 1
     *  自定义 切继承了 JobParametersIncrementer接口的也是
     *  会将最近的 job 相同名字的所有参数读取出来
     *  并放入 JobParameters 参数中，再用 新的覆盖，
     *
     * @param args
     */
    public static void main(String[] args) {
        args = new String[]{"name=张蛟龙3"};//模拟 入参
        System.setProperty("spring.profiles.active","01");//  同理 --spring.profiles.active=xxx

        SpringApplication.run(C_Job的参数JobParameters.class, args);
        System.out.println("***********启动完毕**当前编码:" + Charset.defaultCharset().name() + "****************");

        if(false){
            JobParameters jobParameters = new JobParametersBuilder()
                    .addDate("time", new Date())//添加参数
                    .addDouble("double", new Double(5.5))
                    .addLong("long", new Long(5))
                    .addString("String", "String")
                    .toJobParameters();
        }
        return;
    }
    @Autowired//@EnableBatchProcessing 这个注解帮你从 spring batch 的配置文件中加载了
    private JobLauncher jobLauncher;//Job 启动器

    @Autowired//Job 构造工厂
    private JobBuilderFactory jobBuilderFactory;
    @Autowired//构建 Step
    private StepBuilderFactory stepBuilderFactory;
    @Bean
    public Step step002() throws Exception {
        return stepBuilderFactory
                .get("step002")//Step 工作的名称
                .tasklet(tasklet002())//放入工作内容
                .build();//构建
    }
    @Bean
    public Step step003() throws Exception {
        return stepBuilderFactory
                .get("step003")//Step 工作的名称
                .tasklet(tasklet003())//放入工作内容
                .build();//构建
    }

    @StepScope//延迟加载 在被调用时在加载次 Bean 因为 @Value("#{jobParameters['name']}") 这个动作是调用的时候才会传入
    @Bean//任务 Tasklet 这是最简单的    还有Chunk块处理
    public Tasklet tasklet002(/*@Value("#{jobParameters['name']}") String name*/){
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

                StepExecution stepExecution = contribution.getStepExecution();//获取step的执行
//                ExecutionContext stepExecutionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();
                ExecutionContext stepExecutionContext = stepExecution.getExecutionContext();//获取 step 共享数据
                stepExecutionContext.put("step2","我是步骤222222222222");
                OutColour.out.printlnRed("step 步骤共享数据：" + stepExecutionContext);

                JobExecution jobExecution = stepExecution.getJobExecution();//获取 Job的执行
//                ExecutionContext jobExecutionContext = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
                ExecutionContext jobExecutionContext = jobExecution.getExecutionContext();//获取 job 共享数据
                jobExecutionContext.put("job2","我是作业2222222222");
                OutColour.out.printlnRed("job 作业共享数据：" + jobExecutionContext);

                StepContext stepContext = chunkContext.getStepContext();//获取 Job的 入参，配置 执行者等
                Map<String, Object> jobParameters = stepContext.getJobParameters();//获取 Job的 参数信息 JobParameters
                OutColour.out.printlnRed("Hello SpringBatch 2222222222 初步使用成功....Job的全部参数：" + jobParameters + "\t执行中参数；" +   jobExecution.getStatus());
                return RepeatStatus.FINISHED;//已完成
            }
        };
    }
    @StepScope//延迟加载 在被调用时在加载次 Bean 因为 @Value("#{jobParameters['name']}") 这个动作是调用的时候才会传入
    @Bean//任务 Tasklet 这是最简单的    还有Chunk块处理
    public Tasklet tasklet003(){
        return new Tasklet() {
            /**
             * org.springframework.batch:spring-batch-core:4.3.6
             * 共享变量存入数据库会出现乱码问题， 由于我们能常用的都是 UTF-8
             * 而这个 里的编码强制了 ISO-8859-1
             * @see org.springframework.batch.core.repository.dao.JdbcExecutionContextDao
             * 新版本 5.x 已解决 用的 Charset.defaultCharset().name() 当前编码
             *
             */
            @Override
            public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

                StepExecution stepExecution = contribution.getStepExecution();//获取step的执行
                ExecutionContext stepExecutionContext = stepExecution.getExecutionContext();//获取 step 共享数据
                stepExecutionContext.put("step3","我是步骤333333333333");
                OutColour.out.printlnRed("step 步骤共享数据：" + stepExecutionContext);

                JobExecution jobExecution = stepExecution.getJobExecution();//获取 Job的执行
                ExecutionContext jobExecutionContext = jobExecution.getExecutionContext();//获取 job 共享数据
                jobExecutionContext.put("job3","我是作业3333333333333");
                OutColour.out.printlnRed("job 作业共享数据：" + jobExecutionContext);

                StepContext stepContext = chunkContext.getStepContext();//获取 Job的 入参，配置 执行者等
                Map<String, Object> jobParameters = stepContext.getJobParameters();//获取 Job的 参数信息 JobParameters
                OutColour.out.printlnRed("Hello SpringBatch 333333333333 初步使用成功....Job的全部参数：" + jobParameters + "\t执行中参数；" +   jobExecution.getStatus());
                return RepeatStatus.FINISHED;//已完成
            }
        };
    }
    //配置默认参数校验器
    @Bean
    public DefaultJobParametersValidator defaultValidator(){
        DefaultJobParametersValidator defaultValidator = new DefaultJobParametersValidator();
        defaultValidator.setRequiredKeys(new String[]{"name"});  //必填name
        defaultValidator.setOptionalKeys(new String[]{"age"});   //可选age
        return defaultValidator;
    }
    //配置 自定义的 参数校验器 MyNameParamValidator
    @Bean
    public MyNameParamValidator validator(){
        return new MyNameParamValidator();
    }
    //配置组合参数校验器
    @Bean
    public CompositeJobParametersValidator compositeValidator(){

        DefaultJobParametersValidator defaultValidator = new DefaultJobParametersValidator();
        defaultValidator.setRequiredKeys(new String[]{"name"});  //name必填
        defaultValidator.setOptionalKeys(new String[]{"age"});   //age可选

        MyNameParamValidator nameParamValidator = new MyNameParamValidator();  //name 不能为空 自定义的

        CompositeJobParametersValidator compositeValidator = new CompositeJobParametersValidator();//组合校验器
        //按照传入的顺序，先执行 nameParamValidator 后执行 defaultValidator  按list 顺序便利
        compositeValidator.setValidators(Arrays.asList(nameParamValidator,defaultValidator));//放入多个校验器

        try {
            compositeValidator.afterPropertiesSet();  //判断校验器是否为null
        } catch (Exception e) {
            e.printStackTrace();
        }

        return compositeValidator;//返回组合
    }

    @Bean// 会自己找 指定符合的类传进去入参
    public Job job002(MyNameParamValidator myNameParamValidator) throws Exception {
        return jobBuilderFactory
                .get("job002")//job 工作的名称
                .start(step002())//加入 第一个步骤
                .start(step002())//加入 第一个步骤 如果已存在  会覆盖第一个放入的，
                .next(step003())//加入 步骤 add
//                .validator(validator())//放入自定义校验参数器
//                .validator(myNameParamValidator)//放入自定义校验参数器
//                .validator(defaultValidator())//放入 给的 校验参数器
//                .validator(compositeValidator())//放入 组合 参数器
                .incrementer(new RunIdIncrementer())//放入 作业递增 run.id 参数
//                .incrementer(myParamIncrementer)//放入自定义的 参数方法  继承  JobParametersIncrementer即可

//                .listener(myJobExecutionListener)//放入 作业执行侦听器  会在Job执行前后执行
//                .listener(myAnnotateJobExecutionListener)//放入注解形式的 作业执行侦听器  会在Job执行前后执行
                .listener(JobListenerFactoryBean.getListener(myAnnotateJobExecutionListener))//放入注解形式的  作业执行侦听器  会在Job执行前后执行
                .build();//构建
    }
    @Autowired
    private MyParamIncrementer myParamIncrementer;
    @Autowired
    private MyJobExecutionListener myJobExecutionListener;
    @Autowired
    private MyAnnotateJobExecutionListener myAnnotateJobExecutionListener;
}
@Component//将此类加载到 bean中
class MyNameParamValidator implements JobParametersValidator {//参数校验器 JobParametersValidator
    @Override
    public void validate(@Nullable JobParameters jobParameters) throws JobParametersInvalidException {
        if (jobParameters == null) {
            throw new JobParametersInvalidException("这个 JobParameters 是空的");
        }else{
            String name = jobParameters.getString("name");
            if (name == null || name.trim().length() < 0){
                throw new JobParametersInvalidException("自定义 校验器 -- name参数不能为空");
            }
        }
    }
}